/* **********************************************************************
版本: 2021.1.25f1c1
作者: wzz
日期: 2022-4-8
描述: 基本核心，重复循环使用item，提高效率。
基本功能：
    1.支持格子类或一行一排
    2.支持瞬间跳转到某一个item和动画移动到某个item（支持以view中心点为基点，即item在view的中心点）
    3.自动适配对齐，上下左右格子间隔等 (最常用)
    4.支持item可变大小（一行一个或一排一个）
    5.格子类支持自动适配一行N个item或一排N个item
    6.支持同时存在多种类item
    7.支持一格一格的移动
    8.各种回调（滚动变化回调，滚动停止回调，滚动停止时超屏回调， 一格一格滚动时item变化时回调等等）
    ....

*************************************************************************/

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace FastScrollView
{
    public struct Item
    {
        public GameObject gameObject;
        public RectTransform rectTransform;
        public string name;
        public int index;
    }

    [Serializable]
    public struct Padding
    {
        public float left;
        public float right;
        public float top;
        public float bottom;

        public Padding(float left, float right, float top, float bottom)
        {
            this.left = left;
            this.right = right;
            this.top = top;
            this.bottom = bottom;
        }
    }

    public class FastScrollView : MonoBehaviour, IEndDragHandler, IBeginDragHandler, IDragHandler
    {
        // 滚动类型
        public enum ScrollType
        {
            TopToBottom = 0,
            BottomToTop,
            LeftToRight,
            RightToLeft,
        }

        [SerializeField]
        private List<ItemPrefabConfData> mItemPrefabDataList = new List<ItemPrefabConfData>();

        // item  预设配置
        [Serializable]
        public class ItemPrefabConfData
        {
            public GameObject prefab = null;

            [Tooltip("与下一个item的间隔")]
            public float spacing = 0;
            [Tooltip("垂直时：为x值偏差；水平时：为y值偏差;  0为相应垂直或水平居中")]
            public float offtPos = 0;

            [NonSerialized]
            public float size = 0;

            [NonSerialized]
            public float otherSize = 0;
        }

        [SerializeField]
        public ScrollType scrollType = ScrollType.TopToBottom;

        [Tooltip("第一个item与初始位置的间隔适配，如TopToBottom时，其值等于item列表中，最宽的item的宽边与content的间隙")]
        [SerializeField]
        private bool paddingFit = true;

        [Tooltip("第一个item与初始位置的间隔，如TopToBottom时，表示第一个item与顶部的间隔")]
        [SerializeField]
        private float firstPadding = 0;

        [Tooltip("与firstPadding相对应，表示最后一个，最后位置与边缘的间隔")]
        [SerializeField]
        private float lastPadding = 0;

        [Tooltip("当滚动超出初始/结束位置大于此值时，回调一次")]
        [SerializeField]
        private float scrollExceedDistance = 5f;

        [Tooltip("调用MoveToIndex时的缓动公式")]
        [SerializeField]
        private EaseMode moveToEaseMode = EaseMode.EaseInOut;

        private Dictionary<string, ItemPrefabConfData> mItemPrefabDataDic;

        private ScrollRect scrollRect;
        private RectTransform contentRectTransform;
        private RectTransform viewportRectTransform;

        // 数据
        private int minIndex = 0;
        private int maxIndex = 0;
        private bool isVertical = false;
        private int itemTotalCount = 0;
        private float viewSize = 0;
        private float contentSize = 0;
        private const int distanceOffsetShow = 30; // 显示距离偏差
        private const int distanceOffsetRecycle = 30; // 回收距离偏差
        private float startItemPos = 0;
        private float endItemPos = 0;
        private List<Item> itemList;
        private List<Item> itemTempList;
        private List<Item> itemTempRealList;
        private ItemPosMgr itemPosMgr;
        private itemPoolMgr itemPoolMgr;
        private string defaultItemName = string.Empty;
        private bool isInit = false;
        private float oldMove = 0;
        private float curMove = 0;
        private bool onScrollStopFlag = false;
        private float stopScrollStopVelocity = 5f; // 当速度小于这个值时，则停下来
        private bool onScrollExceedFlag = false;
        private int curFocusIndex = -1;
        private bool focusInCenter = false;
        private PointerEventData mPointerEventData = null;
        private int moveToIndex = 0;
        private bool moveToCenter = false;

        // 各种回调
        private Func<int, string> getItemNameByIndex; // 多种类item创item时，使用
        private Action<int, GameObject> onUpdateItem; // 刷新item时，调用
        private Action onScrollStop; // 当滚动停止时，调用
        private Action onScrollExceedStart; // 当滚动超出初始位置时，调用
        private Action onScrollExceedEnd; // 当滚动超出结束位置时，调用
        private Action<int> onFocusIndexChange; // 当焦点item改变时，调用
        private Action onScrollValueChange; // 当滚动变化时，调用

        #region snap data 相关

        // 缓动类型
        private enum EaseMode
        {
            Average,
            EaseOut,
            EaseIn,
            EaseInOut,
        }

        [Tooltip("是否一格一格的移动")]
        [SerializeField]
        private bool isSnap = true;

        [Tooltip("勾选表示以view中点为基，否则以起点为基")]
        [SerializeField]
        private bool isSnapViewCenter = false;

        [Tooltip("滑动速率小于此值时，开始换成scrollEaseMode的方式滑向目标")]
        [SerializeField]
        private float stopSnapVelocity = 50f;
        private bool isDraging = false;
        private bool hadDealWithSnap = false;
        private float targetSnapPos = 0;
        private float curSnapDuration = 0;
        private Vector2 startSnapPos;
        private bool moveToStart = false;

        [Tooltip("值为0~1，为item的百分比，如0.2时，表示滑动距离超过item的20%的长度，就滑到下一个")]
        [SerializeField]
        private float snapNextItemDistancePer = 0.2f;

        private EaseMode curEaseMode;
        private float startSnapTime = 0; // 开始时间
        private float endSnapTime = 0; // 结束时间
        private bool isMovingToIndex = false;

        [Tooltip("滑动时的缓动公式")]
        [SerializeField]
        private EaseMode scrollEaseMode = EaseMode.EaseInOut;

        [Tooltip("缓动总时间(秒)，最好在0.1~0.5之间")]
        [SerializeField]
        private float snapDuration = 0.15f; // 缓动总时间

        #endregion snap data 相关

        [Tooltip("勾选表示格子是非固定大小")]
        [SerializeField]
        private bool itemNotFixSize = false;

        #region grid 相关

        [SerializeField]
        [Tooltip("勾选表示格子类型")]
        private bool isGrid = false;

        [Tooltip("自动适配一行或一列item的数量")]
        [SerializeField]
        private bool fitOneColOrRowItemCount = false; // 适配一行或一列item的数量

        [Tooltip("一行或一列item的数量")]
        [SerializeField]
        private int oneColOrRowItemCount = 2; // 一个队列的数量

        [Tooltip("适配最外围格子与content的左右边距为Grid Spacing的值，滚动为垂直时，取格子的左右间隔")]
        [SerializeField]
        private bool fitGridPaddingLR = true;

        [Tooltip("适配最外围格子与content的上下边距Grid Spacing的值，滚动为垂直时，取格子的左右间隔")]
        [SerializeField]
        private bool fitGridPaddingTB = true;

        [Tooltip("将其中的一个值，调整为另一个值，如滚动为垂直时，将Y调整为X一样的值")]
        [SerializeField]
        private bool fitGridSpacing = true;

        // 格子对齐方式
        public enum GridAlignment
        {
            TopToBottom = 0,
            BottomToTop,
            LeftToRight,
            RightToLeft,
        }

        [Tooltip("格子的对齐方式")]
        [SerializeField]
        private GridAlignment gridAlignment = GridAlignment.LeftToRight;

        [Tooltip("最外围格子与content的边距，勾选Fit One Col Or Row Item Count会自动调整其中一个值。如滚动为垂直时，会调整X")]
        [SerializeField]
        private Padding gridPadding = new Padding(0, 0, 0, 0);

        [Tooltip("格子与格子的间隔，若勾选Fit One Col Or Row Item Count会取此值作为最小值进行间隔适配")]
        [SerializeField]
        private Vector2 gridSpacing = Vector2.zero;

        #endregion grid 相关

        /// <summary>
        /// 初始化
        /// </summary>
        public void Init(Action<int, GameObject> action)
        {
            if (isInit)
            {
                Debug.LogError("只能初始化一次");
                return;
            }

            isInit = true;
            itemList = new List<Item>();
            itemTempList = new List<Item>();
            itemTempRealList = new List<Item>();
            mItemPrefabDataDic = new Dictionary<string, ItemPrefabConfData>();

            onUpdateItem = action;
            isVertical = scrollType == ScrollType.BottomToTop || scrollType == ScrollType.TopToBottom;

            ReviseCacheConfig();

            InitScrollRect();
            InitItemPrefab();
            InitScrollbar();

            itemPosMgr = new ItemPosMgr(firstPadding, lastPadding, scrollType);
            itemPoolMgr = new itemPoolMgr(mItemPrefabDataList);

            InitGrid();
        }

        private void InitGrid()
        {
            if (!(isGrid && CheckGridConfig()))
            {
                return;
            }

            if (fitOneColOrRowItemCount)
            {
                // 适配一行或一列item的数量
                float size = isVertical ? contentRectTransform.rect.width : contentRectTransform.rect.height;

                float itemSize = mItemPrefabDataDic[defaultItemName].otherSize;
                float spaceing = isVertical ? gridSpacing.x : gridSpacing.y;
                float newSpaceing = spaceing;

                float num;

                if ((fitGridPaddingLR && isVertical) || (!isVertical && fitGridPaddingTB))
                {
                    num = Mathf.Floor(size / (itemSize + spaceing));
                    newSpaceing = (size - num * itemSize) / (num + 1);
                    if (newSpaceing < spaceing)
                    {
                        num--;
                        newSpaceing = (size - num * itemSize) / (num + 1);
                    }
                }
                else
                {
                    if (isVertical)
                    {
                        size -= (gridPadding.left + gridPadding.right);
                    }
                    else
                    {
                        size -= (gridPadding.top + gridPadding.bottom);
                    }

                    num = Mathf.Floor(size / (itemSize + spaceing));
                    newSpaceing = (size - num * itemSize) / (num - 1);
                    if (newSpaceing < spaceing)
                    {
                        num--;
                        newSpaceing = (size - num * itemSize) / (num - 1);
                    }
                }

                oneColOrRowItemCount = (int)num;

                if (isVertical)
                {
                    gridSpacing.x = newSpaceing;
                }
                else
                {
                    gridSpacing.y = newSpaceing;
                }

                if (fitGridSpacing)
                {
                    if (isVertical)
                    {
                        gridSpacing.y = gridSpacing.x;
                    }
                    else
                    {
                        gridSpacing.x = gridSpacing.y;
                    }
                }

                if (fitGridPaddingLR)
                {
                    gridPadding.left = isVertical ? gridSpacing.x : gridSpacing.y;
                    gridPadding.right = gridPadding.left;
                }

                if (fitGridPaddingTB)
                {
                    gridPadding.top = isVertical ? gridSpacing.x : gridSpacing.y;
                    gridPadding.bottom = gridPadding.top;
                }
            }

            mItemPrefabDataDic[defaultItemName].spacing = isVertical ? gridSpacing.y : gridSpacing.x;
            itemPosMgr.InitGrid(gridAlignment, oneColOrRowItemCount, ref gridPadding, ref gridSpacing);
        }

        /// <summary>
        /// 检查格子相应配置
        /// </summary>
        /// <returns></returns>
        private bool CheckGridConfig()
        {
            if (!isGrid)
                return true;

            if (mItemPrefabDataDic.Count != 1)
            {
                Debug.LogError("格子目前只支持单一的item");
                return false;
            }

            return true;
        }

        /// <summary>
        /// 当scroll view 会动态改变大小时，需要调用此函数（如父节点有 Vertical Layout Group等动态修改scroll view大小）
        /// </summary>
        public void ResetViewSize()
        {
            if (isVertical)
            {
                viewSize = viewportRectTransform.rect.height;
            }
            else
            {
                viewSize = viewportRectTransform.rect.width;
            }

            if (itemNotFixSize)
            {
                var index = itemList[itemList.Count - 1].index + 1;
                if (index > itemTotalCount)
                    return;

                AddItemToFront(index);

                contentSize = itemPosMgr.GetTotalSize();

                // 重设 content 大小
                ResetContentSize();

                ResetAllShowItemPos();
                return;
            }

            // 计算显示的item最大最小位置
            CalculateShowMinAndMaxPos(isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x);

            // 计算显示的item最大最小index
            CalculateShowMinAndMaxIndex(itemList[itemList.Count - 1].index);

            ShowAddItems();
        }

        /// <summary>
        /// 修正缓存配置
        /// </summary>
        private void ReviseCacheConfig()
        {
            if (isGrid)
            {
                if (isVertical)
                {
                    if (gridAlignment == GridAlignment.TopToBottom || gridAlignment == GridAlignment.BottomToTop)
                        gridAlignment = GridAlignment.LeftToRight;
                }
                else
                {
                    if (gridAlignment == GridAlignment.LeftToRight || gridAlignment == GridAlignment.RightToLeft)
                        gridAlignment = GridAlignment.TopToBottom;
                }
            }
        }

        /// <summary>
        /// 初始化ScrollRect
        /// </summary>
        private void InitScrollRect()
        {
            scrollRect = transform.GetComponent<ScrollRect>();
            contentRectTransform = scrollRect.content;
            viewportRectTransform = scrollRect.viewport;
            scrollRect.onValueChanged.AddListener(OnValueChanged);

            scrollRect.vertical = isVertical;
            scrollRect.horizontal = !isVertical;

            if (isVertical)
            {
                viewSize = viewportRectTransform.rect.height;
            }
            else
            {
                viewSize = viewportRectTransform.rect.width;
            }
            AdjustContentPivotAndAnchor();
        }

        /// <summary>
        /// 初始化item预设
        /// </summary>
        private void InitItemPrefab()
        {
            if (mItemPrefabDataList.Count == 0)
            {
                Debug.LogError("请设置item配置");
                return;
            }
            else if (mItemPrefabDataList.Count == 1)
            {
                defaultItemName = mItemPrefabDataList[0].prefab.name;
            }

            mItemPrefabDataDic.Clear();
            RectTransform rectTransform;
            float maxSize = 0;
            foreach (var item in mItemPrefabDataList)
            {
                rectTransform = item.prefab.GetComponent<RectTransform>();

                if (isVertical)
                {
                    item.size = rectTransform.rect.height;
                    item.otherSize = rectTransform.rect.width;
                    maxSize = Mathf.Max(maxSize, rectTransform.rect.width);
                }
                else
                {
                    item.size = rectTransform.rect.width;
                    item.otherSize = rectTransform.rect.height;
                    maxSize = Mathf.Max(maxSize, rectTransform.rect.height);
                }

                if (itemNotFixSize)
                {
                    RebuildRect(rectTransform, true);
                    item.size = isVertical ? Mathf.Max(50, rectTransform.rect.height) : Mathf.Max(50, rectTransform.rect.width);
                }
                item.prefab.SetActive(false);
                AdjustItemPivotAndAnchor(rectTransform);
                mItemPrefabDataDic.Add(item.prefab.name, item);
            }
            if (paddingFit)
            {
                firstPadding = isVertical ? (contentRectTransform.rect.width - maxSize) * 0.5f : (contentRectTransform.rect.height - maxSize) * 0.5f;
                lastPadding = firstPadding;
            }
        }

        /// <summary>
        /// 初始化Scrollbar
        /// </summary>
        private void InitScrollbar()
        {
            if (scrollRect.horizontalScrollbarVisibility == ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport && scrollRect.horizontalScrollbar != null)
            {
                Debug.LogError("不支持： AutoHideAndExpandViewport");
            }
            if (scrollRect.verticalScrollbarVisibility == ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport && scrollRect.verticalScrollbar != null)
            {
                Debug.LogError("不支持： AutoHideAndExpandViewport");
            }
        }

        /// <summary>
        /// 展示需要显示的items
        /// </summary>
        private void ShowNeedItems()
        {
            for (int i = minIndex; i <= maxIndex; i++)
            {
                AddItem(i);
            }
        }

        /// <summary>
        /// 展示需要显示的items
        /// </summary>
        private void ShowAddItems()
        {
            var index = itemList[itemList.Count - 1].index;
            for (int i = index + 1; i <= maxIndex; i++)
            {
                AddItem(i);
            }
        }

        /// <summary>
        /// 设置item 位置
        /// </summary>
        private void SetItemPos(Item item)
        {
            if (isGrid)
            {
                item.rectTransform.anchoredPosition = itemPosMgr.GetItemPos(item.index);
                return;
            }

            if (scrollType == ScrollType.LeftToRight)
            {
                Util.SetAnchoredPosition4(item.rectTransform, itemPosMgr.GetItemPos(item.index));
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                Util.SetAnchoredPosition6(item.rectTransform, itemPosMgr.GetItemPos(item.index));
            }
            else if (scrollType == ScrollType.TopToBottom)
            {
                Util.SetAnchoredPosition8(item.rectTransform, itemPosMgr.GetItemPos(item.index));
            }
            else
            {
                Util.SetAnchoredPosition2(item.rectTransform, itemPosMgr.GetItemPos(item.index));
            }
        }

        /// <summary>
        /// 通过index获取Item名
        /// </summary>
        private string GetItemNameByIndex(int index)
        {
            string itemName = defaultItemName;
            if (string.IsNullOrEmpty(itemName))
            {
                if (getItemNameByIndex == null)
                {
                    Debug.LogError("多种类item, 请在初始化时，设置SetGetItemNameByIndex函数");
                    return null;
                }
                itemName = getItemNameByIndex.Invoke(index);
            }
            return itemName;
        }

        /// <summary>
        /// 当列表有多种类item时，需要通过index取相应的item
        /// </summary>
        public void ItemNameByIndexFunc(Func<int, string> func)
        {
            getItemNameByIndex = func;
        }

        /// <summary>
        ///  滚动item时，滚动停下来时，调用, Jump和Move都会调用一次，但SetItemCount引起的变化不会调用
        /// </summary>
        public void SetOnScrollStop(Action action)
        {
            onScrollStop = action;
        }

        /// <summary>
        /// 当滚动超出初始位置时，调用
        /// 如：TopToBottom时，往下拉到尽，y值小于0，并绝对值大于scrollExceedDistance时，调用
        /// </summary>
        public void SetOnScrollExceedStart(Action action)
        {
            onScrollExceedStart = action;
        }

        /// <summary>
        /// 与SetOnScrollExceedStart相反，表示结束位置拉尽
        /// </summary>
        public void SetOnScrollExceedEnd(Action action)
        {
            onScrollExceedEnd = action;
        }

        /// <summary>
        /// 当焦点item index改变时回调，注意：若content 小于view时，返回0
        /// </summary>
        /// <param name="action">回调函数</param>
        /// <param name="isCenter">是否是以界面中心点为基准</param>
        public void SetOnFocusIndexChange(Action<int> action, bool isCenter = false)
        {
            onFocusIndexChange = action;
            focusInCenter = isCenter;
        }

        /// <summary>
        /// 设置滚动变化回调
        /// </summary>
        public void SetOnScrollValueChange(Action action)
        {
            onScrollValueChange = action;
        }

        /// <summary>
        /// 滚动层 大小变化
        /// </summary>
        public void OnScrollRectSizeChange()
        {
            if (isVertical)
            {
                viewSize = viewportRectTransform.rect.height;
            }
            else
            {
                viewSize = viewportRectTransform.rect.width;
            }
            InitGrid();
        }

        /// <summary>
        /// 当item是可变大小时，调用
        /// </summary>
        private void SetItemCountWhenItemNotFixSize(int count, bool resetPos, bool updateItem)
        {
            int oldMinIndex = minIndex;
            int oldMaxIndex = maxIndex;
            int focusIndex = CalculateCurFocusIndex(false);

            // 回收所有item
            RecycleAllItem();
            RecycleAllTempItem();

            StopSnapAction();
            scrollRect.StopMovement();
            isMovingToIndex = false;

            itemTotalCount = count;

            if (count <= 0)
            {
                return;
            }

            if (!updateItem)
            {
                // 重设所有位置数据
                ResetPosData();
                return;
            }

            if (oldMaxIndex >= itemTotalCount - 1 && !resetPos)
            {
                // 滚到最后一个，刷新都是需要滚到低（可根据实际情况修改此处代码）
                ResetPosData();
                JumpToIndexWhenItemNotFixSize(itemTotalCount - 1, false);
                return;
            }

            if (resetPos)
            {
                // 重设所有位置数据
                ResetPosData();

                curMove = 0;
                ResetContentPos(true);
                CalculateShowMinAndMaxPos(curMove);
                minIndex = 0;
                maxIndex = 0;
            }
            else
            {
                minIndex = focusIndex > itemTotalCount - 1 ? itemTotalCount - 1 : focusIndex;
                maxIndex = minIndex;

                ResetPosData(minIndex + 1);

                contentSize = itemPosMgr.GetTotalSize();
                ResetContentSize();
                ResetContentPos(false);
                CalculateShowMinAndMaxPos(isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x);
            }

            AddItemToFront(minIndex);
            AddItemToBack(minIndex);

            contentSize = itemPosMgr.GetTotalSize();

            // 重设content大小
            ResetContentSize();

            ResetAllShowItemPos();

            // 焦点item变化
            curFocusIndex = -1;
            OnFocusIndexChange();

            OnScrollValueChange();
        }

        /// <summary>
        /// 向前添加item
        /// </summary>
        private void AddItemToFront(int index)
        {
            bool hadInView = false;
            Item item;
            ItemPos itemPos;
            do
            {
                itemPos = itemPosMgr.GetItem(index);
                maxIndex = index;
                item = AddItem(index);
                RebuildRect(item.rectTransform);
                itemPos = itemPosMgr.SetItemSize(index, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                itemPosMgr.SetItemPosAndNext(ref itemPos);

                index++;
                if (index == itemTotalCount)
                    break;

                if (curMove == 0)
                {
                    itemPos = itemPosMgr.GetItem(index);
                    if (!isNeedShow(itemPos))
                        break;
                }
                else
                {
                    contentSize = itemPosMgr.GetTotalSize();
                    ResetContentSize();

                    ResetContentPos(false);
                    CalculateShowMinAndMaxPos(isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x);

                    if (!isNeedShow(itemPos))
                    {
                        hadInView = true;
                        break;
                    }

                    itemPos = itemPosMgr.GetItem(index);
                    if (!isNeedShow(itemPos))
                    {
                        hadInView = true;
                        break;
                    }
                    else if (hadInView)
                    {
                        break;
                    }
                }
            } while (index < itemTotalCount);
            itemPosMgr.ResetPos(maxIndex + 1);
        }

        /// <summary>
        /// 向后添加item
        /// </summary>
        private void AddItemToBack(int index)
        {
            Item item;
            ItemPos itemPos;

            index = minIndex;
            float oldContentSize;
            for (int i = index - 1; i >= 0; i--)
            {
                curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                CalculateShowMinAndMaxPos(curMove);

                itemPos = itemPosMgr.GetItem(i);
                if (!isNeedShow(itemPos))
                    break;

                minIndex = i;
                item = AddItem(i);
                RebuildRect(item.rectTransform);
                itemPos = itemPosMgr.SetItemSize(i, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                itemPosMgr.SetItemPos(ref itemPos);

                oldContentSize = contentSize;
                contentSize = itemPosMgr.GetTotalSize();
                ResetContentSize();

                Vector2 pos = contentRectTransform.anchoredPosition;
                if (isVertical)
                    pos.y += oldContentSize - contentSize;
                else
                    pos.x += oldContentSize - contentSize;
                SetContentAnchoredPosition(pos);
            }
            itemPosMgr.ResetPos(minIndex + 1);
        }

        /// <summary>
        /// 通过传数量即可刷新item列表
        /// </summary>
        /// <param name="count">item的数量</param>
        /// <param name="resetPos">是否重置位置</param>
        /// <param name="updateItem">如果默认显示特定位置的item，请传false配合JumpToIndex使用,可少刷新一次</param>
        public void SetItemCount(int count, bool resetPos = false, bool updateItem = true)
        {
            if (itemTotalCount == 0)
                resetPos = true;

            if (itemNotFixSize)
            {
                SetItemCountWhenItemNotFixSize(count, resetPos, updateItem);
                return;
            }

            // 回收所有item
            RecycleAllItem();
            RecycleAllTempItem();
            StopSnapAction();
            isMovingToIndex = false;
            oldMove = 0;

            if (count <= 0)
            {
                itemTotalCount = 0;
                return;
            }

            // 大小数量不一样
            if (!(count == itemTotalCount && !string.IsNullOrEmpty(defaultItemName)))
            {
                itemTotalCount = count;

                // 重设所有位置数据
                ResetPosData();

                // 重设content大小
                ResetContentSize();
            }

            if (!updateItem)
                return;

            // 重设content位置
            ResetContentPos(resetPos);

            // 计算显示的item最大最小位置
            CalculateShowMinAndMaxPos(isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x);

            // 计算显示的item最大最小index
            CalculateShowMinAndMaxIndex(GetShowItemRandomIndex(resetPos));

            // 显示item
            ShowNeedItems();

            // 焦点item变化
            curFocusIndex = -1;
            OnFocusIndexChange();

            OnScrollValueChange();
        }

        /// <summary>
        /// 可变item, 瞬移到某个位置
        /// </summary>
        private void JumpToIndexWhenItemNotFixSize(int index, bool isCenter = false)
        {
            // 回收所有item
            RecycleAllItem();
            RecycleAllTempItem();

            minIndex = index;
            maxIndex = index;

            Item item = AddItem(minIndex, false);
            RebuildRect(item.rectTransform);
            ItemPos itemPos = itemPosMgr.SetItemSize(minIndex, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
            itemPosMgr.SetItemPos(ref itemPos);
            itemPosMgr.ResetPos(minIndex + 1);

            for (int i = index - 1; i >= 0; i--)
            {
                contentSize = itemPosMgr.GetTotalSize();
                CalculateShowMinAndMaxPos(CalculateContentPanlMove(index, isCenter));

                itemPos = itemPosMgr.GetItem(i);
                if (!isNeedShow(itemPos))
                    break;

                minIndex = i;
                item = AddItem(minIndex, true);
                RebuildRect(item.rectTransform);
                itemPos = itemPosMgr.SetItemSize(minIndex, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                itemPosMgr.SetItemPos(ref itemPos);
            }
            itemPosMgr.ResetPos(minIndex + 1);

            for (int i = index + 1; i < itemTotalCount; i++)
            {
                contentSize = itemPosMgr.GetTotalSize();
                CalculateShowMinAndMaxPos(CalculateContentPanlMove(index, isCenter));

                itemPos = itemPosMgr.GetItem(i);
                if (!isNeedShow(itemPos))
                    break;

                maxIndex = i;
                item = AddItem(maxIndex, false);
                RebuildRect(item.rectTransform);
                itemPos = itemPosMgr.SetItemSize(maxIndex, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                itemPosMgr.SetItemPosAndNext(ref itemPos);
            }

            itemPosMgr.ResetPos(maxIndex + 1);

            // 重设content大小
            contentSize = itemPosMgr.GetTotalSize();
            ResetContentSize();

            // 重设所有显示item位置
            ResetAllShowItemPos();

            Vector2 pos = contentRectTransform.anchoredPosition;
            if (isVertical)
            {
                pos.y = CalculateContentPanlMove(index, isCenter);
            }
            else
            {
                pos.x = CalculateContentPanlMove(index, isCenter);
            }
            SetContentAnchoredPosition(pos);

            CalculateShowMinAndMaxPos(isVertical ? pos.y : pos.x);

            curFocusIndex = -1;
            OnFocusIndexChange();
            OnScrollValueChange();
        }

        /// <summary>
        /// 瞬移到某个位置
        /// </summary>
        /// <param name="index">0~item最大数</param>
        /// <param name="isCenter">是否将移动的对象，显示在中间</param>
        public void JumpToIndex(int index, bool isCenter = false)
        {
            if (itemTotalCount == 0)
                return;
            if (index < 0)
            {
                index = 0;
            }
            else if (index >= itemTotalCount)
            {
                index = itemTotalCount - 1;
            }

            isMovingToIndex = false;
            onScrollStopFlag = false;
            StopSnapAction();
            scrollRect.StopMovement();

            if (itemNotFixSize)
            {
                JumpToIndexWhenItemNotFixSize(index, isCenter);
                return;
            }

            if (viewSize >= contentSize && itemList.Count > 0)
                return;

            Vector2 pos = contentRectTransform.anchoredPosition;
            if (isVertical)
            {
                pos.y = CalculateContentPanlMove(index, isCenter);
            }
            else
            {
                pos.x = CalculateContentPanlMove(index, isCenter);
            }
            contentRectTransform.anchoredPosition = pos;

            RecycleAllItem();
            RecycleAllTempItem();

            CalculateShowMinAndMaxPos(isVertical ? pos.y : pos.x);
            CalculateShowMinAndMaxIndex(index);
            ShowNeedItems();
            OnFocusIndexChange();

            OnScrollValueChange();
        }

        /// <summary>
        /// 移动到某个位置
        /// </summary>
        /// <param name="index">下标，从0开始</param>
        /// <param name="isCenter">true:表示以view的中心点为对齐</param>
        /// <param name="speed">速度</param>
        /// <param name="multiple">倍数，若为0，表示不添加temp item 从当前至index中的所有item都刷新</param>
        public void MoveToIndex(int index, bool isCenter = false, float speed = 1000f, int multiple = 2)
        {
            if (itemTotalCount == 0)
                return;
            if (itemNotFixSize)
            {
                Debug.LogError("可变item 不支持MoveToIndex， 或者使用JumpToIndex，或者自行修改代码");
                return;
            }

            if (viewSize >= contentSize && itemList.Count > 0)
                return;

            if (index < 0)
                index = 0;
            else if (index >= itemTotalCount)
                index = itemTotalCount - 1;

            moveToIndex = index;
            moveToCenter = isCenter;

            curEaseMode = moveToEaseMode;
            isMovingToIndex = false;
            onScrollStopFlag = false;
            StopSnapAction();
            scrollRect.StopMovement();

            var curMinIndex = minIndex;
            var curMaxIndex = maxIndex;

            targetSnapPos = CalculateContentPanlMove(index, isCenter);
            CalculateShowMinAndMaxPos(targetSnapPos);
            CalculateShowMinAndMaxIndex(index);
            float distance;
            bool toStart = IsToStart(targetSnapPos);

            int showNum = curMaxIndex - curMinIndex + 1;

            int spaceingNum = toStart ? minIndex - curMaxIndex - 1 : curMinIndex - maxIndex - 1;
            if (multiple >= 1 && spaceingNum > showNum * multiple)
            {
                // 需要添加temp
                int startIndex = toStart ? curMaxIndex + 1 : curMinIndex - 1;
                spaceingNum = showNum * multiple;
                AddTempItem(toStart, startIndex, spaceingNum);

                itemTempRealList.Clear();
                int firstIndex;
                int lastIndex;
                if (toStart)
                {
                    firstIndex = startIndex + spaceingNum;
                    lastIndex = firstIndex + showNum - 1;
                    int realIndex = minIndex;
                    for (int i = firstIndex; i <= lastIndex; i++)
                    {
                        MoveToIndexAddItem(i, realIndex);
                        realIndex++;
                        if (realIndex > maxIndex)
                            break;
                    }

                    targetSnapPos = CalculateContentPanlMove(GetTempTargetIndex(minIndex, showNum, index), isCenter);
                    distance = CalculateDistance(targetSnapPos);
                }
                else
                {
                    firstIndex = startIndex - spaceingNum;
                    lastIndex = firstIndex - showNum - 1;
                    int realIndex = maxIndex;
                    for (int i = firstIndex; i > lastIndex; i--)
                    {
                        MoveToIndexAddItem(i, realIndex, true);
                        realIndex--;
                        if (realIndex < minIndex)
                            break;
                    }

                    targetSnapPos = CalculateContentPanlMove(firstIndex, isCenter);
                    distance = CalculateDistance(targetSnapPos);
                }
            }
            else
            {
                // 不用添加temp item, 全显
                if (toStart)
                {
                    for (int i = curMaxIndex + 1; i <= maxIndex; i++)
                        AddItem(i);
                }
                else
                {
                    for (int i = curMinIndex - 1; i >= minIndex; i--)
                        AddItem(i, true);
                }
                distance = CalculateDistance(targetSnapPos);
            }

            isMovingToIndex = true;
            //Debug.Break();
            StartSnapAction(distance / speed);
        }

        /// <summary>
        /// MoveToIndex 结束
        /// </summary>
        private void MoveToIndexEnd()
        {
            if (!isMovingToIndex)
                return;

            isMovingToIndex = false;

            ItemTempRealListToItemList();
            ContentPanlMove(moveToIndex, moveToCenter);
            RecycleAllTempItem();
        }

        /// <summary>
        /// 是否在开始位置，TopToBottom时，指顶，LeftToRight时，指左
        /// </summary>
        public bool InStart(float offt = 10f)
        {
            if (scrollType == ScrollType.TopToBottom)
            {
                if (contentRectTransform.anchoredPosition.y <= offt)
                {
                    return true;
                }
            }
            else if (scrollType == ScrollType.BottomToTop)
            {
                if (contentRectTransform.anchoredPosition.y >= -offt)
                {
                    return true;
                }
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                if (contentRectTransform.anchoredPosition.x >= -offt)
                {
                    return true;
                }
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                if (contentRectTransform.anchoredPosition.x <= offt)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 是否在结束位置，TopToBottom时，指低，LeftToRight时，指右
        /// </summary>
        public bool InEnd(float offt = 10f)
        {
            if (scrollType == ScrollType.TopToBottom)
            {
                if (contentSize - viewSize - offt <= contentRectTransform.anchoredPosition.y)
                {
                    return true;
                }
            }
            else if (scrollType == ScrollType.BottomToTop)
            {
                if (viewSize - contentSize + offt >= contentRectTransform.anchoredPosition.y)
                {
                    return true;
                }
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                if (viewSize - contentSize + offt >= contentRectTransform.anchoredPosition.x)
                {
                    return true;
                }
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                if (contentSize - viewSize - offt <= contentRectTransform.anchoredPosition.x)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 立即重新刷新layout
        /// </summary>
        private void RebuildRect(RectTransform rectTransform, bool allChild = true)
        {
            if (allChild)
            {
                RectTransform[] list = rectTransform.GetComponentsInChildren<RectTransform>();
                for (int i = list.Length - 1; i >= 0; i--)
                {
                    LayoutRebuilder.ForceRebuildLayoutImmediate(list[i]);
                }
            }
            LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform);
        }

        /// <summary>
        /// 调整item的中心点和锚点
        /// </summary>
        private void AdjustItemPivotAndAnchor(RectTransform rectTransform)
        {
            if (isGrid)
            {
                AdjustGridItemPivotAndAnchor(rectTransform);
                return;
            }
            Vector2 anchorMin = rectTransform.anchorMin;
            Vector2 anchorMax = rectTransform.anchorMax;
            if (scrollType == ScrollType.BottomToTop)
            {
                if (anchorMin.y != anchorMax.y)
                {
                    anchorMin.y = 0;
                    anchorMax.y = 0;
                }
            }
            else if (scrollType == ScrollType.TopToBottom)
            {
                if (anchorMin.y != anchorMax.y)
                {
                    anchorMin.y = 1;
                    anchorMax.y = 1;
                }
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                if (anchorMin.x != anchorMax.x)
                {
                    anchorMin.x = 0;
                    anchorMax.x = 0;
                }
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                if (anchorMin.x != anchorMax.x)
                {
                    anchorMin.x = 1;
                    anchorMax.x = 1;
                }
            }
            rectTransform.anchorMin = anchorMin;
            rectTransform.anchorMax = anchorMax;
        }

        /// <summary>
        /// 调整格子item的中心点和锚点
        /// </summary>
        private void AdjustGridItemPivotAndAnchor(RectTransform rectTransform)
        {
            Vector2 pivot = rectTransform.pivot;
            Vector2 anchorMin = rectTransform.anchorMin;
            Vector2 anchorMax = rectTransform.anchorMax;

            if (scrollType == ScrollType.TopToBottom)
            {
                if (gridAlignment == GridAlignment.LeftToRight)
                {
                    // 以左上角对齐
                    pivot.x = 0f;
                    pivot.y = 1f;
                    anchorMin.x = 0f;
                    anchorMax.x = 0f;
                    anchorMin.y = 1f;
                    anchorMax.y = 1f;
                }
                else
                {
                    //gridAlignment == GridAlignment.RightToLeft
                    // 以右上角对齐
                    pivot.x = 1f;
                    pivot.y = 1f;
                    anchorMin.x = 1f;
                    anchorMax.x = 1f;
                    anchorMin.y = 1f;
                    anchorMax.y = 1f;
                }
            }
            else if (scrollType == ScrollType.BottomToTop)
            {
                if (gridAlignment == GridAlignment.LeftToRight)
                {
                    // 以左下角对齐
                    pivot.x = 0f;
                    pivot.y = 0f;
                    anchorMin.x = 0f;
                    anchorMax.x = 0f;
                    anchorMin.y = 0f;
                    anchorMax.y = 0f;
                }
                else
                {
                    //gridAlignment == GridAlignment.RightToLeft
                    // 以右下角对齐
                    pivot.x = 1f;
                    pivot.y = 0;
                    anchorMin.x = 1f;
                    anchorMax.x = 1f;
                    anchorMin.y = 0f;
                    anchorMax.y = 0f;
                }
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                if (gridAlignment == GridAlignment.TopToBottom)
                {
                    // 以左上角对齐
                    pivot.x = 0f;
                    pivot.y = 1f;
                    anchorMin.x = 0f;
                    anchorMax.x = 0f;
                    anchorMin.y = 1f;
                    anchorMax.y = 1f;
                }
                else
                {
                    //gridAlignment == GridAlignment.BottomToTop
                    // 以左下角对齐
                    pivot.x = 0f;
                    pivot.y = 0f;
                    anchorMin.x = 0f;
                    anchorMax.x = 0f;
                    anchorMin.y = 0f;
                    anchorMax.y = 0f;
                }
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                if (gridAlignment == GridAlignment.TopToBottom)
                {
                    // 以右上角对齐
                    pivot.x = 1f;
                    pivot.y = 1f;
                    anchorMin.x = 1f;
                    anchorMax.x = 1f;
                    anchorMin.y = 1f;
                    anchorMax.y = 1f;
                }
                else
                {
                    //gridAlignment == GridAlignment.BottomToTop
                    // 以右下角对齐
                    pivot.x = 1f;
                    pivot.y = 0;
                    anchorMin.x = 1f;
                    anchorMax.x = 1f;
                    anchorMin.y = 0f;
                    anchorMax.y = 0f;
                }
            }
            rectTransform.pivot = pivot;
            rectTransform.anchorMin = anchorMin;
            rectTransform.anchorMax = anchorMax;
        }

        /// <summary>
        /// 调整content 锚点和中心点
        /// </summary>
        private void AdjustContentPivotAndAnchor()
        {
            Vector2 pivot = contentRectTransform.pivot;
            Vector2 anchorMin = contentRectTransform.anchorMin;
            Vector2 anchorMax = contentRectTransform.anchorMax;
            if (scrollType == ScrollType.BottomToTop)
            {
                pivot.y = 0;
                anchorMin.y = 0;
                anchorMax.y = 0;
            }
            else if (scrollType == ScrollType.TopToBottom)
            {
                pivot.y = 1;
                anchorMin.y = 1;
                anchorMax.y = 1;
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                pivot.x = 0;
                anchorMin.x = 0;
                anchorMax.x = 0;
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                pivot.x = 1;
                anchorMin.x = 1;
                anchorMax.x = 1;
            }
            contentRectTransform.pivot = pivot;
            contentRectTransform.anchorMin = anchorMin;
            contentRectTransform.anchorMax = anchorMax;
        }

        /// <summary>
        /// 计算content需要移动的位置
        /// </summary>
        private float CalculateContentPanlMove(int index, bool isViewCenter)
        {
            if (index == 0)
                return 0;

            if (contentSize - viewSize <= 0)
                return 0;

            float toMove = 0;
            ItemPos itemPos = itemPosMgr.GetItem(index);
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                toMove = itemPos.startPos;
                if (isViewCenter)
                {
                    toMove += viewSize * 0.5f - itemPos.size * 0.5f;
                }
                else
                {
                    itemPos = itemPosMgr.GetItem(index - 1);
                    toMove += itemPos.spacing * 0.5f;
                }

                toMove = Mathf.Clamp(-toMove, 0, contentSize - viewSize);
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                toMove = itemPos.startPos;
                if (isViewCenter)
                {
                    toMove -= viewSize * 0.5f - itemPos.size * 0.5f;
                }
                else
                {
                    itemPos = itemPosMgr.GetItem(index - 1);
                    toMove -= itemPos.spacing * 0.5f;
                }
                toMove = Mathf.Clamp(-toMove, viewSize - contentSize, 0);
            }
            return toMove;
        }

        /// <summary>
        /// content移动到相应index
        /// </summary>
        private Vector2 ContentPanlMove(int index, bool isCenter)
        {
            Vector2 pos = contentRectTransform.anchoredPosition;
            if (isVertical)
            {
                pos.y = CalculateContentPanlMove(index, isCenter);
            }
            else
            {
                pos.x = CalculateContentPanlMove(index, isCenter);
            }
            contentRectTransform.anchoredPosition = pos;
            return pos;
        }

        /// <summary>
        /// 计算显示的item最小最大位置
        /// </summary>
        private void CalculateShowMinAndMaxPos(float curMove)
        {
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                startItemPos = -curMove + distanceOffsetShow;
                endItemPos = -curMove - viewSize - distanceOffsetShow;
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                startItemPos = -curMove - distanceOffsetShow;
                endItemPos = -curMove + viewSize + distanceOffsetShow;
            }
        }

        /// <summary>
        /// 计算显示的item index最小最大位置, index一定是显示范围中的有效index
        /// </summary>
        private void CalculateShowMinAndMaxIndex(int index)
        {
            minIndex = index;
            maxIndex = index;
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                ItemPos itemPos;
                for (int i = index; i >= 0; i--)
                {
                    itemPos = itemPosMgr.GetItem(i);
                    if (itemPos.endPos > startItemPos)
                        break;
                    minIndex = i;
                }
                for (int i = index + 1; i < itemTotalCount; i++)
                {
                    itemPos = itemPosMgr.GetItem(i);
                    if (itemPos.startPos < endItemPos)
                        break;
                    maxIndex = i;
                }
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                ItemPos itemPos;
                for (int i = index; i >= 0; i--)
                {
                    itemPos = itemPosMgr.GetItem(i);
                    if (itemPos.endPos < startItemPos)
                        break;
                    minIndex = i;
                }
                for (int i = index + 1; i < itemTotalCount; i++)
                {
                    itemPos = itemPosMgr.GetItem(i);
                    if (itemPos.startPos > endItemPos)
                        break;
                    maxIndex = i;
                }
            }
        }

        /// <summary>
        /// 计算当前焦点item index
        /// </summary>
        private int CalculateCurFocusIndex(bool isCenter)
        {
            // todo:与CalculateSnapNearestItemIndex极相似，相办法改一下

            int nearestIndex = 0;
            ItemPos itemPos;
            if (minIndex == maxIndex)
            {
                nearestIndex = minIndex;
            }
            else if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                float curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                float front, back;
                float focusPos = isCenter ? -(curMove + viewSize * 0.5f) : -curMove;
                for (int i = minIndex; i <= maxIndex; i++)
                {
                    itemPos = itemPosMgr.GetItem(i);
                    itemPosMgr.GetItemFrontAndBackMargin(i, out front, out back);

                    if (itemPos.startPos + front >= focusPos && itemPos.endPos - back < focusPos)
                    {
                        nearestIndex = i;
                        break;
                    }
                }
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                float curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                float front, back;
                float focusPos = isCenter ? -curMove + viewSize * 0.5f : -curMove;
                for (int i = minIndex; i <= maxIndex; i++)
                {
                    itemPos = itemPosMgr.GetItem(i);
                    itemPosMgr.GetItemFrontAndBackMargin(i, out front, out back);

                    if (itemPos.startPos - front <= focusPos && itemPos.endPos + back > focusPos)
                    {
                        nearestIndex = i;
                        break;
                    }
                }
            }
            return nearestIndex;
        }

        /// <summary>
        /// 计算聚点位置，在相对于 item 的哪个位置，以开始指向结束为正方向，[0, 1] 为在item中 <0为在反方向，>1 为正方向
        /// </summary>
        private float CalculateFocusPosInItemPer(bool isCenter, int index)
        {
            float focusPos;
            float front, back, startPos, endPos;
            float curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
            ItemPos itemPos = itemPosMgr.GetItem(index);
            itemPosMgr.GetItemFrontAndBackMargin(index, out front, out back);
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                focusPos = isCenter ? -(curMove + viewSize * 0.5f) : -curMove;

                startPos = itemPos.startPos + front;
                endPos = itemPos.endPos - back;
            }
            else //(scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                focusPos = isCenter ? -curMove + viewSize * 0.5f : -curMove;
                startPos = itemPos.startPos - front;
                endPos = itemPos.endPos + back;
            }
            return (focusPos - startPos) / (endPos - startPos);
        }

        /// <summary>
        /// 计算最近的item index
        /// </summary>
        private int CalculateSnapNearestItemIndex(bool isViewCenter)
        {
            int nearestIndex = 0;
            ItemPos itemPos;
            if (minIndex == maxIndex)
            {
                nearestIndex = minIndex;
            }
            else if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                float curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;

                float minDistance = 9999999;
                float curDistance = 0;
                for (int i = minIndex; i < maxIndex; i++)
                {
                    itemPos = itemPosMgr.GetItem(i);

                    if (isViewCenter)
                    {
                        curDistance = Mathf.Abs((itemPos.startPos + itemPos.endPos) * 0.5f + viewSize * 0.5f + curMove);
                    }
                    else
                    {
                        curDistance = Mathf.Abs(itemPos.startPos + curMove);
                    }

                    if (minDistance >= curDistance)
                    {
                        minDistance = curDistance;
                        nearestIndex = i;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                float curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;

                float minDistance = 9999999;
                float curDistance = 0;
                for (int i = minIndex; i < maxIndex; i++)
                {
                    itemPos = itemPosMgr.GetItem(i);

                    if (isViewCenter)
                    {
                        curDistance = Mathf.Abs((itemPos.startPos + itemPos.endPos) * 0.5f - viewSize * 0.5f + curMove);
                    }
                    else
                    {
                        curDistance = Mathf.Abs(itemPos.startPos + curMove);
                    }

                    if (minDistance >= curDistance)
                    {
                        minDistance = curDistance;
                        nearestIndex = i;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return nearestIndex;
        }

        /// <summary>
        /// 调整content临时位置，MoveToIndex效果需要
        /// </summary>
        private void AdjustContentTempPos(out bool toStart, out float distance)
        {
            distance = 0;
            toStart = true;
            Vector2 curPos = contentRectTransform.anchoredPosition;
            if (scrollType == ScrollType.TopToBottom)
            {
                toStart = targetSnapPos > curPos.y;
                distance = Mathf.Abs(targetSnapPos - curPos.y);
                if (distance > viewSize)
                {
                    distance = viewSize;
                    if (toStart)
                    {
                        curPos.y = targetSnapPos - viewSize;
                    }
                    else
                    {
                        curPos.y = targetSnapPos + viewSize;
                    }
                }
            }
            else if (scrollType == ScrollType.BottomToTop)
            {
                toStart = targetSnapPos < curPos.y;
                distance = Mathf.Abs(targetSnapPos - curPos.y);
                if (distance > viewSize)
                {
                    distance = viewSize;
                    if (toStart)
                    {
                        curPos.y = targetSnapPos + viewSize;
                    }
                    else
                    {
                        curPos.y = targetSnapPos - viewSize;
                    }
                }
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                toStart = targetSnapPos < curPos.x;
                distance = Mathf.Abs(targetSnapPos - curPos.x);
                if (distance > viewSize)
                {
                    distance = viewSize;
                    if (toStart)
                    {
                        curPos.x = targetSnapPos + viewSize;
                    }
                    else
                    {
                        curPos.x = targetSnapPos - viewSize;
                    }
                }
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                toStart = targetSnapPos > curPos.x;
                distance = Mathf.Abs(targetSnapPos - curPos.x);
                if (distance > viewSize)
                {
                    distance = viewSize;
                    if (toStart)
                    {
                        curPos.x = targetSnapPos - viewSize;
                    }
                    else
                    {
                        curPos.x = targetSnapPos + viewSize;
                    }
                }
            }
            contentRectTransform.anchoredPosition = curPos;
        }

        /// <summary>
        /// 计算目标位置与当前位置的距离位置
        /// </summary>
        private float CalculateDistance(float targetSnapPos)
        {
            float distance = 0;
            Vector2 curPos = contentRectTransform.anchoredPosition;
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.BottomToTop)
            {
                distance = Mathf.Abs(targetSnapPos - curPos.y);
            }
            else
            {
                distance = Mathf.Abs(targetSnapPos - curPos.x);
            }

            if (distance > viewSize)
            {
                distance = viewSize;
            }
            return distance;
        }

        /// <summary>
        /// 返回true，表示往开始的方向移动
        /// </summary>
        private bool IsToStart(float targetSnapPos)
        {
            bool toStart = true;
            Vector2 curPos = contentRectTransform.anchoredPosition;
            if (scrollType == ScrollType.TopToBottom)
            {
                toStart = targetSnapPos > curPos.y;
            }
            else if (scrollType == ScrollType.BottomToTop)
            {
                toStart = targetSnapPos < curPos.y;
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                toStart = targetSnapPos < curPos.x;
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                toStart = targetSnapPos > curPos.x;
            }

            return toStart;
        }

        /// <summary>
        /// 添加临时显示item,不走item刷新,MoveToIndex效果需要
        /// </summary>
        private void AddTempItem(bool toStart, int startIndex, int num)
        {
            string itemName = GetItemNameByIndex(startIndex);
            if (toStart)
            {
                for (int i = startIndex; i < startIndex + num; i++)
                {
                    Item item = itemPoolMgr.PopItem(itemName);
                    item.index = i;
                    SetItemPos(item);
                    AddTempItem(item);
                }
                return;
            }
            for (int i = startIndex; i > startIndex - num; i--)
            {
                Item item = itemPoolMgr.PopItem(itemName);
                item.index = i;
                SetItemPos(item);
                AddTempItem(item);
            }
        }

        /// <summary>
        /// 回收所有临时显示item
        /// </summary>
        private void RecycleAllTempItem()
        {
            for (int i = 0; i < itemTempList.Count; i++)
            {
                itemPoolMgr.PushItem(itemTempList[i]);
            }
            itemTempList.Clear();
        }

        /// <summary>
        /// 添加一个item, true表示往列表的首位添加
        /// </summary>
        private Item AddItem(int index, bool isFirst = false)
        {
            if (itemNotFixSize)
            {
                for (int i = 0; i < itemList.Count; i++)
                {
                    if (itemList[i].index == index)
                        return itemList[i];
                }
            }
            string itemName = GetItemNameByIndex(index);
            Item item = itemPoolMgr.PopItem(itemName);
            item.index = index;
            SetItemPos(item);

            if (isFirst)
            {
                itemList.Insert(0, item);
            }
            else
            {
                itemList.Add(item);
            }

            onUpdateItem.Invoke(index, item.gameObject);
            return item;
        }

        /// <summary>
        /// MoveToIndex 时添加item, 后续需要重设回原来实际的item
        /// </summary>
        private void MoveToIndexAddItem(int index, int realIndex, bool isFirst = false)
        {
            string itemName = GetItemNameByIndex(index);
            Item item = itemPoolMgr.PopItem(itemName);
            item.index = index;
            SetItemPos(item);

            if (isFirst)
            {
                itemTempRealList.Insert(0, item);
            }
            else
            {
                itemTempRealList.Add(item);
            }

            onUpdateItem.Invoke(realIndex, item.gameObject);
        }

        /// <summary>
        /// 将 item temp 转成 item list 下
        /// </summary>
        private void ItemTempRealListToItemList()
        {
            if (itemTempRealList.Count == 0)
                return;

            foreach (var item in itemList)
            {
                itemPoolMgr.PushItem(item);
            }
            itemList.Clear();

            int index = 0;
            for (int i = minIndex; i <= maxIndex; i++)
            {
                if (index >= itemTempRealList.Count)
                {
                    maxIndex = i - 1;
                    break;
                }
                Item item = itemTempRealList[index];
                item.index = i;
                SetItemPos(item);
                itemList.Add(item);
                index++;
            }
            itemTempRealList.Clear();
        }

        /// <summary>
        /// 获取 临时 item 列表中，对应目标的 index
        /// </summary>
        private int GetTempTargetIndex(int firstIndex, int showNum, int toIndex)
        {
            int index = toIndex - firstIndex;

            return itemTempList[itemTempList.Count - showNum + index].index;
        }

        /// <summary>
        /// 添加临时item
        /// </summary>
        private void AddTempItem(Item item)
        {
            itemTempList.Add(item);
        }

        /// <summary>
        /// 通过itemList index回收item
        /// </summary>
        private void RecycleItem(int listIndex)
        {
            itemPoolMgr.PushItem(itemList[listIndex]);
            itemList.RemoveAt(listIndex);
        }

        /// <summary>
        /// instart为true,表示在开始处回收，否则为在结束处回收
        /// </summary>
        private void RecycleItem(bool inStart)
        {
            Item item;
            ItemPos itemPos;
            if (inStart)
            {
                int Count = itemList.Count;
                // 在开始处回收
                for (int i = 0; i < Count; i++)
                {
                    item = itemList[0];
                    itemPos = itemPosMgr.GetItem(item.index);
                    if (!isNeedRecycle(itemPos))
                        break;

                    RecycleItem(0);
                }
                return;
            }

            // 在结束处回收
            for (int i = itemList.Count - 1; i >= 0; i--)
            {
                item = itemList[i];
                itemPos = itemPosMgr.GetItem(item.index);
                if (!isNeedRecycle(itemPos))
                    break;

                RecycleItem(itemList.Count - 1);
            }
        }

        /// <summary>
        /// 回收所有item
        /// </summary>
        private void RecycleAllItem()
        {
            minIndex = 0;
            maxIndex = 0;

            foreach (var item in itemList)
            {
                itemPoolMgr.PushItem(item);
            }
            itemList.Clear();
        }

        /// <summary>
        /// 修正minIndex和maxIndex
        /// </summary>
        private void ReviseMinAndMaxIndex()
        {
            if (itemList.Count == 0)
                return;

            minIndex = itemList[0].index;
            maxIndex = itemList[itemList.Count - 1].index;
        }

        /// <summary>
        /// 重设scroll view Content 大小
        /// </summary>
        private void ResetContentSize()
        {
            if (isVertical)
            {
                if (contentRectTransform.rect.height != contentSize)
                {
                    contentRectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, contentSize);
                }
            }
            else
            {
                if (contentRectTransform.rect.width != contentSize)
                {
                    contentRectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, contentSize);
                }
            }
        }

        /// <summary>
        /// 重设scroll view Content 位置
        /// </summary>
        private void ResetContentPos(bool resetPos)
        {
            Vector2 curPos = contentRectTransform.anchoredPosition;
            if (resetPos)
            {
                if (isVertical)
                {
                    curPos.y = 0;
                }
                else
                {
                    curPos.x = 0;
                }
                SetContentAnchoredPosition(curPos);
                return;
            }

            if (scrollType == ScrollType.TopToBottom)
            {
                if (curPos.y > contentSize - viewSize)
                {
                    curPos.y = Mathf.Max(0, contentSize - viewSize);
                }
            }
            else if (scrollType == ScrollType.BottomToTop)
            {
                if (curPos.y < viewSize - contentSize)
                {
                    curPos.y = Mathf.Min(0, viewSize - contentSize);
                }
            }
            else if (scrollType == ScrollType.LeftToRight)
            {
                if (curPos.x < viewSize - contentSize)
                {
                    curPos.x = Mathf.Min(0, viewSize - contentSize);
                }
            }
            else if (scrollType == ScrollType.RightToLeft)
            {
                if (curPos.x > contentSize - viewSize)
                {
                    curPos.x = Mathf.Max(0, contentSize - viewSize);
                }
            }
            SetContentAnchoredPosition(curPos);
        }

        /// <summary>
        /// 设置 scroll view Content 位置
        /// </summary>
        private void SetContentAnchoredPosition(Vector2 pos)
        {
            if (pos == contentRectTransform.anchoredPosition)
                return;

            contentRectTransform.anchoredPosition = pos;
        }

        /// <summary>
        /// 获取显示item中的任意一个index
        /// </summary>
        private int GetShowItemRandomIndex(bool resetPos)
        {
            if (resetPos)
            {
                return 0;
            }

            int amount = itemTotalCount;
            int lastMinIndex = 0;
            int lastMaxIndex = itemTotalCount - 1;
            int index = lastMaxIndex / 2;
            ItemPos itemPos;
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                while (amount > 0)
                {
                    amount--;
                    itemPos = itemPosMgr.GetItem(index);
                    if (itemPos.startPos >= endItemPos && itemPos.endPos <= startItemPos)
                    {
                        return index;
                    }
                    else if (itemPos.endPos > startItemPos)
                    {
                        lastMinIndex = index;
                        index = (lastMaxIndex + lastMinIndex) / 2;
                    }
                    else
                    {
                        lastMaxIndex = index;
                        index = (lastMaxIndex + lastMinIndex) / 2;
                    }
                }
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                while (amount > 0)
                {
                    amount--;
                    itemPos = itemPosMgr.GetItem(index);
                    if (itemPos.startPos <= endItemPos && itemPos.endPos >= startItemPos)
                    {
                        return index;
                    }
                    else if (itemPos.endPos < startItemPos)
                    {
                        lastMinIndex = index;
                        index = (lastMaxIndex + lastMinIndex) / 2;
                    }
                    else
                    {
                        lastMaxIndex = index;
                        index = (lastMaxIndex + lastMinIndex) / 2;
                    }
                }
            }

            return 0;
        }

        /// <summary>
        /// 重设所有位置数据
        /// </summary>
        private void ResetPosData(int startIndex = 0)
        {
            ItemPrefabConfData data;
            string itemName;

            itemPosMgr.Clear(startIndex);

            itemPosMgr.SetItemCount(itemTotalCount);
            for (int i = startIndex; i < itemTotalCount; i++)
            {
                itemName = GetItemNameByIndex(i);

                if (!mItemPrefabDataDic.TryGetValue(itemName, out data))
                {
                    Debug.Log($"index={i}, 没有此itemName={itemName}");
                    return;
                }
                ItemPos itemPos = new ItemPos();
                itemPos.name = data.prefab.name;
                itemPos.index = i;
                itemPos.size = data.size;
                itemPos.otherSize = data.otherSize;
                itemPos.spacing = data.spacing;
                itemPos.offtPos = data.offtPos;
                itemPosMgr.SetItemPos(ref itemPos);
            }

            contentSize = itemPosMgr.CalculateTotalSize();
        }

        /// <summary>
        /// 重置所有显示item的位置
        /// </summary>
        private void ResetAllShowItemPos()
        {
            foreach (var val in itemList)
            {
                SetItemPos(val);
            }
        }

        /// <summary>
        /// 判断是否需要显示
        /// </summary>
        private bool isNeedShow(ItemPos itemPos)
        {
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                return itemPos.startPos >= endItemPos && itemPos.endPos <= startItemPos;
            }

            // scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight
            return itemPos.startPos <= endItemPos && itemPos.endPos >= startItemPos;
        }

        /// <summary>
        /// 判断是否需要回收
        /// </summary>
        private bool isNeedRecycle(ItemPos itemPos)
        {
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                return !(itemPos.startPos >= endItemPos - distanceOffsetRecycle && itemPos.endPos <= startItemPos + distanceOffsetRecycle);
            }

            // scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight
            return !(itemPos.startPos <= endItemPos + distanceOffsetRecycle && itemPos.endPos >= startItemPos - distanceOffsetRecycle);
        }

        /// <summary>
        /// moveToStart 表示向开始处划
        /// </summary>
        private void UpdateTopToBottm(bool moveToStart = true)
        {
            ItemPos itemPos;
            Item item;
            int amount;
            RecycleItem(moveToStart);

            bool haveChange = false;
            if (moveToStart)
            {
                // 在结尾处添加
                amount = itemTotalCount;
                while (amount > 0 && maxIndex + 1 < itemTotalCount)
                {
                    amount--;
                    itemPos = itemPosMgr.GetItem(maxIndex + 1);
                    if (itemPos.endPos > startItemPos)
                    {
                        maxIndex++;
                    }
                    else if (isNeedShow(itemPos))
                    {
                        maxIndex++;
                        item = AddItem(maxIndex);

                        haveChange = true;
                        if (!itemNotFixSize)
                            continue;

                        RebuildRect(item.rectTransform);
                        itemPos = itemPosMgr.SetItemSize(maxIndex, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                        itemPosMgr.SetItemPosAndNext(ref itemPos);
                        SetItemPos(itemList[itemList.Count - 1]);
                    }
                    else
                    {
                        break;
                    }
                }

                if (itemList.Count == 0)
                    minIndex = 0;
                else
                    minIndex = itemList[0].index;

                if (itemNotFixSize && haveChange)
                {
                    itemPosMgr.ResetPos(minIndex + 1);

                    // 重设content大小
                    contentSize = itemPosMgr.GetTotalSize();
                    ResetContentSize();

                    // 本来不需要回收的，但添加item后，相对位置变化，可能需要回收
                    RecycleItem(moveToStart);
                    ReviseMinAndMaxIndex();

                    curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                    oldMove = curMove;
                    CalculateShowMinAndMaxPos(curMove);
                }

                return;
            }

            //  在开始处添加
            amount = itemTotalCount;
            haveChange = false;
            while (amount > 0 && minIndex > 0)
            {
                amount--;
                itemPos = itemPosMgr.GetItem(minIndex - 1);
                if (itemPos.startPos < endItemPos)
                {
                    minIndex--;
                }
                else if (isNeedShow(itemPos))
                {
                    minIndex--;
                    item = AddItem(minIndex, true);

                    haveChange = true;

                    if (!itemNotFixSize)
                        continue;

                    RebuildRect(item.rectTransform);
                    itemPos = itemPosMgr.SetItemSize(minIndex, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                    itemPosMgr.SetItemPos(ref itemPos);

                    float oldContentSize = contentSize;
                    contentSize = itemPosMgr.GetTotalSize();
                    ResetContentSize();

                    Vector2 pos = contentRectTransform.anchoredPosition;
                    if (isVertical)
                        pos.y += contentSize - oldContentSize;
                    else
                        pos.x += contentSize - oldContentSize;
                    SetContentAnchoredPosition(pos);

                    curMove = isVertical ? pos.y : pos.x;
                    CalculateShowMinAndMaxPos(curMove);
                }
                else
                {
                    break;
                }
            }

            if (itemList.Count == 0)
                maxIndex = 0;
            else
                maxIndex = itemList[itemList.Count - 1].index;

            if (itemNotFixSize && haveChange)
            {
                itemPosMgr.ResetPos(minIndex + 1);

                // 本来不需要回收的，但添加item后，相对位置变化，可能需要回收
                RecycleItem(moveToStart);
                ReviseMinAndMaxIndex();

                // 刷新所有item位置
                ResetAllShowItemPos();

                if (isDraging)
                {
                    scrollRect.OnBeginDrag(mPointerEventData);
                    scrollRect.Rebuild(CanvasUpdate.PostLayout);
                }

                curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                oldMove = curMove;
                CalculateShowMinAndMaxPos(curMove);
            }
        }

        /// <summary>
        /// moveToStart 表示向开始处划
        /// </summary>
        private void UpdateBottomToTop(bool moveToStart = true)
        {
            ItemPos itemPos;
            Item item;
            int amount;
            RecycleItem(moveToStart);

            bool haveChange = false;
            if (moveToStart)
            {
                //  在结尾处添加
                amount = itemTotalCount;
                while (amount > 0 && maxIndex + 1 < itemTotalCount)
                {
                    amount--;
                    itemPos = itemPosMgr.GetItem(maxIndex + 1);
                    if (itemPos.endPos < startItemPos)
                    {
                        maxIndex++;
                    }
                    else if (isNeedShow(itemPos))
                    {
                        maxIndex++;
                        item = AddItem(maxIndex);
                        haveChange = true;
                        if (!itemNotFixSize)
                            continue;

                        RebuildRect(item.rectTransform);
                        itemPos = itemPosMgr.SetItemSize(maxIndex, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                        itemPosMgr.SetItemPosAndNext(ref itemPos);
                        SetItemPos(itemList[itemList.Count - 1]);
                    }
                    else
                    {
                        break;
                    }
                }

                if (itemList.Count == 0)
                    minIndex = 0;
                else
                    minIndex = itemList[0].index;

                if (itemNotFixSize && haveChange)
                {
                    itemPosMgr.ResetPos(minIndex + 1);

                    // 重设content大小
                    contentSize = itemPosMgr.GetTotalSize();
                    ResetContentSize();

                    // 本来不需要回收的，但添加item后，相对位置变化，可能需要回收
                    RecycleItem(moveToStart);
                    ReviseMinAndMaxIndex();

                    curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                    oldMove = curMove;
                    CalculateShowMinAndMaxPos(curMove);
                }

                return;
            }

            // 在开始处添加
            amount = itemTotalCount;
            haveChange = false;
            while (amount > 0 && minIndex > 0)
            {
                amount--;
                itemPos = itemPosMgr.GetItem(minIndex - 1);
                if (itemPos.startPos > endItemPos)
                {
                    minIndex--;
                }
                else if (isNeedShow(itemPos))
                {
                    minIndex--;
                    item = AddItem(minIndex, true);
                    haveChange = true;

                    if (!itemNotFixSize)
                        continue;

                    RebuildRect(item.rectTransform);
                    itemPos = itemPosMgr.SetItemSize(minIndex, isVertical ? item.rectTransform.rect.height : item.rectTransform.rect.width);
                    itemPosMgr.SetItemPos(ref itemPos);

                    float oldContentSize = contentSize;
                    contentSize = itemPosMgr.GetTotalSize();
                    ResetContentSize();

                    Vector2 pos = contentRectTransform.anchoredPosition;
                    if (isVertical)
                        pos.y += oldContentSize - contentSize;
                    else
                        pos.x += oldContentSize - contentSize;
                    SetContentAnchoredPosition(pos);

                    curMove = isVertical ? pos.y : pos.x;
                    CalculateShowMinAndMaxPos(curMove);
                }
                else
                {
                    break;
                }
            }

            if (itemList.Count == 0)
                maxIndex = 0;
            else
                maxIndex = itemList[itemList.Count - 1].index;

            if (itemNotFixSize && haveChange)
            {
                itemPosMgr.ResetPos(minIndex + 1);

                // 本来不需要回收的，但添加item后，相对位置变化，可能需要回收
                RecycleItem(moveToStart);
                ReviseMinAndMaxIndex();

                // 刷新所有item位置
                ResetAllShowItemPos();

                if (isDraging)
                {
                    scrollRect.OnBeginDrag(mPointerEventData);
                    scrollRect.Rebuild(CanvasUpdate.PostLayout);
                }

                curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                oldMove = curMove;
                CalculateShowMinAndMaxPos(curMove);
            }
        }

        /// <summary>
        /// 滚动是否超过范围
        /// </summary>
        /// <returns></returns>
        private bool IsScrollExceed()
        {
            float curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
            bool isExceed = false;
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                if (curMove < 0)
                {
                    isExceed = true;
                    if (curMove < -scrollExceedDistance)
                        OnScrollExceedStart();
                }
                else if (curMove > contentSize - viewSize)
                {
                    isExceed = true;
                    if (curMove > contentSize - viewSize + scrollExceedDistance)
                        OnScrollExceedEnd();
                }
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                if (curMove > 0)
                {
                    isExceed = true;
                    if (curMove > scrollExceedDistance)
                    {
                        OnScrollExceedStart();
                    }
                }
                else if (curMove < viewSize - contentSize)
                {
                    isExceed = true;
                    if (curMove < viewSize - contentSize - scrollExceedDistance)
                        OnScrollExceedEnd();
                }
            }
            return isExceed;
        }

        #region 滚动回调

        private void OnValueChanged(Vector2 pos)
        {
            // 不要用 scrollRect.velocity判断，scrollbar划动时，速度为0
            // 少了一次刷新，但效果极好
            if (oldMove == 0)
            {
                oldMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
                return;
            }

            curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                moveToStart = curMove > oldMove;
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                moveToStart = curMove < oldMove;
            }
            oldMove = curMove;

            if (viewSize >= contentSize || itemTotalCount == 0 || isMovingToIndex)
            {
                return;
            }

            float velocity = Mathf.Abs(isVertical ? scrollRect.velocity.y : scrollRect.velocity.x);
            if (!isSnap && !isDraging && velocity <= stopScrollStopVelocity)
            {
                OnScrollStop();
            }

            if (IsScrollExceed())
            {
                return;
            }

            if (isSnap && !isGrid && !(hadDealWithSnap || isDraging) && velocity < stopSnapVelocity && velocity > 0)
            {
                StartScrollSnap(moveToStart);
                return;
            }

            curMove = isVertical ? contentRectTransform.anchoredPosition.y : contentRectTransform.anchoredPosition.x;
            oldMove = curMove;
            CalculateShowMinAndMaxPos(curMove);

            if (scrollType == ScrollType.TopToBottom || scrollType == ScrollType.RightToLeft)
            {
                UpdateTopToBottm(moveToStart);
            }
            else if (scrollType == ScrollType.BottomToTop || scrollType == ScrollType.LeftToRight)
            {
                UpdateBottomToTop(moveToStart);
            }

            OnFocusIndexChange();
            OnScrollValueChange();
        }

        /// <summary>
        /// 滚动停下
        /// </summary>
        private void OnScrollStop()
        {
            if (onScrollStopFlag)
            {
                return;
            }
            onScrollStopFlag = true;
            scrollRect.StopMovement();
            onScrollStop?.Invoke();
        }

        /// <summary>
        /// 当焦点item变化时，回调
        /// </summary>
        private void OnFocusIndexChange()
        {
            if (onFocusIndexChange == null)
                return;

            int index = CalculateCurFocusIndex(focusInCenter);
            if (index == curFocusIndex)
                return;

            curFocusIndex = index;
            onFocusIndexChange.Invoke(curFocusIndex);
        }

        /// <summary>
        /// 当滚动变化时，回调
        /// </summary>
        private void OnScrollValueChange()
        {
            if (onScrollStopFlag)
                return;
            onScrollValueChange?.Invoke();
        }

        /// <summary>
        /// 当前处理，考虑了加速移动，手放开后，最终拉扯超屏触发
        /// 若需求为，以手放开瞬间判断，则只需要将函数放在OnEndDrag中
        /// </summary>
        private void OnScrollExceedStart()
        {
            if (onScrollExceedFlag)
            {
                return;
            }
            onScrollExceedFlag = true;
            onScrollExceedStart?.Invoke();
        }

        /// <summary>
        /// 与OnScrollExceedStart相对
        /// </summary>
        private void OnScrollExceedEnd()
        {
            if (onScrollExceedFlag)
            {
                return;
            }
            onScrollExceedFlag = true;
            onScrollExceedEnd?.Invoke();
        }

        public void OnBeginDrag(PointerEventData eventData)
        {
            if (itemTotalCount == 0)
                return;

            if (isSnap)
            {
                CalCulateStopSnapVelocity();
            }

            // todo: scrollbar移动时，不会走这里
            oldMove = 0;
            hadDealWithSnap = false;
            isDraging = true;
            onScrollStopFlag = false;
            onScrollExceedFlag = false;
            CacheDragPointerEventData(eventData);
        }

        void IEndDragHandler.OnEndDrag(PointerEventData eventData)
        {
            if (itemTotalCount == 0)
                return;

            mPointerEventData = null;
            isDraging = false;
            if (!isSnap || isGrid)
            {
                return;
            }

            if (viewSize >= contentSize || itemTotalCount == 0 || isMovingToIndex)
            {
                return;
            }
            float velocity = isVertical ? scrollRect.velocity.y : scrollRect.velocity.x;
            if (Mathf.Abs(velocity) > stopSnapVelocity || IsScrollExceed())
            {
                return;
            }
            StartScrollSnap(moveToStart, true);
        }

        public virtual void OnDrag(PointerEventData eventData)
        {
            if (itemTotalCount == 0)
                return;

            if (eventData.button != PointerEventData.InputButton.Left)
            {
                return;
            }
            CacheDragPointerEventData(eventData);
        }

        private void CacheDragPointerEventData(PointerEventData eventData)
        {
            if (mPointerEventData == null)
            {
                mPointerEventData = new PointerEventData(EventSystem.current);
            }
            mPointerEventData.button = eventData.button;
            mPointerEventData.position = eventData.position;
            mPointerEventData.pointerPressRaycast = eventData.pointerPressRaycast;
            mPointerEventData.pointerCurrentRaycast = eventData.pointerCurrentRaycast;
        }

        /// <summary>
        /// 根据距离 计算需要的初始惯性速度
        /// </summary>
        private float CalculateInitialInertiaSpeed(float distance)
        {
            var deltaTime = 0.002f;// Time.unscaledDeltaTime;
            float decelerationRate = scrollRect.decelerationRate;
            float pow = Mathf.Pow(decelerationRate, deltaTime);
            float speed = 0;
            while (distance > 0)
            {
                distance -= speed * deltaTime;
                speed = distance / deltaTime;
                speed = speed / pow;
            }
            return -speed;
        }

        /// <summary>
        /// 计算停止snap速度
        /// </summary>
        private void CalCulateStopSnapVelocity()
        {
            if (!string.IsNullOrEmpty(defaultItemName))
            {
                // 单个固定大小
                ItemPos itemPos = itemPosMgr.GetItem(0);
                float distance = itemPos.size + itemPos.spacing;
                stopSnapVelocity = CalculateInitialInertiaSpeed(distance) + 10;
                return;
            }

            if (itemNotFixSize)
            {
                // 非固定大小
                return;
            }

            // 多个固定大小
        }

        #endregion 滚动回调

        #region Snap处理

        /// <summary>
        /// 开始Snap处理
        /// </summary>
        private void StartScrollSnap(bool moveToStart, bool isOnDragEnd = false)
        {
            hadDealWithSnap = true;
            curEaseMode = scrollEaseMode;
            scrollRect.StopMovement();

            int curIndex = CalculateCurFocusIndex(isSnapViewCenter);
            float per = CalculateFocusPosInItemPer(isSnapViewCenter, curIndex);
            float offt = snapNextItemDistancePer;
            float toStartPer = offt;
            int targetIndex = curIndex;
            if (isOnDragEnd)
            {
                if (moveToStart)
                {
                    if (isSnapViewCenter)
                        toStartPer += 0.5f;
                    if (per > toStartPer)
                        targetIndex = curIndex + 1;
                }
                else
                {
                    if (isSnapViewCenter)
                    {
                        if (per < (0.5 - toStartPer))
                            targetIndex = curIndex - 1;
                    }
                    else
                    {
                    }
                    if (per > (1 - toStartPer))
                        targetIndex = curIndex + 1;
                }
            }
            else
            {
                if (moveToStart)
                {
                    targetIndex = curIndex + 1;
                }
            }
            if (targetIndex < 0 || targetIndex >= itemTotalCount)
                return;
            targetSnapPos = CalculateContentPanlMove(targetIndex, isSnapViewCenter);
            StartSnapAction(snapDuration);
        }

        /// <summary>
        /// 开始Snap动画
        /// </summary>
        private void StartSnapAction(float duration)
        {
            StopSnapAction();
            curSnapDuration = duration;
            startSnapPos = contentRectTransform.anchoredPosition;
            startSnapTime = Time.realtimeSinceStartup;
            endSnapTime = startSnapTime + curSnapDuration;
            InvokeRepeating("UpdateSnapAction", 0, 0.01f);
        }

        /// <summary>
        /// 停止snap动画
        /// </summary>
        private void StopSnapAction()
        {
            CancelInvoke("UpdateSnapAction");
        }

        /// <summary>
        /// update snap动画
        /// </summary>
        private void UpdateSnapAction()
        {
            if (Time.realtimeSinceStartup >= endSnapTime)
            {
                StopSnapAction();
                SnapActionContentPos(1);

                OnScrollStop();
                OnFocusIndexChange();
                OnScrollValueChange();
                MoveToIndexEnd();
                return;
            }
            float per = 1;
            switch (curEaseMode)
            {
                case EaseMode.Average:
                    per = AverageQuad(Time.realtimeSinceStartup - startSnapTime, 0, 1, curSnapDuration);
                    break;

                case EaseMode.EaseOut:
                    per = EaseInQuad(Time.realtimeSinceStartup - startSnapTime, 0, 1, curSnapDuration);
                    break;

                case EaseMode.EaseIn:
                    per = EaseOutQuad(Time.realtimeSinceStartup - startSnapTime, 0, 1, curSnapDuration);
                    break;

                case EaseMode.EaseInOut:
                    per = EaseInOutQuad(Time.realtimeSinceStartup - startSnapTime, 0, 1, curSnapDuration);
                    break;
            }
            SnapActionContentPos(per);
            OnScrollValueChange();
            return;
        }

        /// <summary>
        /// snap 动画 content位置
        /// </summary>
        private void SnapActionContentPos(float per)
        {
            Vector2 toPos;
            if (isVertical)
            {
                toPos = new Vector2(startSnapPos.x, startSnapPos.y + per * (targetSnapPos - startSnapPos.y));
            }
            else
            {
                toPos = new Vector2(startSnapPos.x + per * (targetSnapPos - startSnapPos.x), startSnapPos.y);
            }
            contentRectTransform.anchoredPosition = toPos;
        }

        #endregion Snap处理

        #region 缓动函数

        /// <summary>
        /// 平均速度
        /// </summary>
        private float AverageQuad(float t, float b, float c, float d)
        {
            return t / d;
        }

        /// <summary>
        /// 开始时速度很慢, 然后逐渐加快
        /// </summary>
        private float EaseInQuad(float t, float b, float c, float d)
        {
            var x = t / d;
            var y = x * x;
            return b + c * y;
        }

        /// <summary>
        /// 开始时速度很快,给人一种流畅感.然后逐渐减速
        /// </summary>
        private float EaseOutQuad(float t, float b, float c, float d)
        {
            var x = t / d;
            var y = -x * x + 2 * x;
            return b + c * y;
        }

        /// <summary>
        /// 开始时速度很快,给人一种流畅感.然后逐渐减速, 动画时间不宜过长, 最好在300~500ms间.
        /// </summary>
        private float EaseInOutQuad(float t, float b, float c, float d)
        {
            if (t < d / 2)
            {
                return EaseInQuad(t, b, c / 2, d / 2);
            }
            else
            {
                var t1 = t - d / 2;
                var b1 = b + c / 2;
                return EaseInOutQuad(t1, b1, c / 2, d / 2);
            }
        }

        # endregion
    }

    #region item 缓存池管理

    public class itemPoolMgr
    {
        private Dictionary<string, FastScrollView.ItemPrefabConfData> itemPrefabDic;
        private Dictionary<string, Queue<Item>> itemPoolDic;

        public itemPoolMgr(List<FastScrollView.ItemPrefabConfData> list)
        {
            itemPoolDic = new Dictionary<string, Queue<Item>>();
            itemPrefabDic = new Dictionary<string, FastScrollView.ItemPrefabConfData>();
            foreach (var item in list)
            {
                itemPrefabDic.Add(item.prefab.name, item);
                itemPoolDic.Add(item.prefab.name, new Queue<Item>());
            }
        }

        /// <summary>
        ///  弹出一个item,没有会创建
        /// </summary>
        public Item PopItem(string itemName)
        {
            Item item;
            if (itemPoolDic[itemName].Count == 0)
            {
                item = new Item();
                FastScrollView.ItemPrefabConfData itemConf = itemPrefabDic[itemName];
                item.gameObject = GameObject.Instantiate(itemConf.prefab, itemConf.prefab.transform.parent);
                item.rectTransform = item.gameObject.transform.GetComponent<RectTransform>();
                item.name = itemName;
            }
            else
            {
                item = itemPoolDic[itemName].Dequeue();
            }
            item.gameObject.SetActive(true);
            return item;
        }

        /// <summary>
        ///  回收一个item
        /// </summary>
        public void PushItem(Item item)
        {
            item.gameObject.SetActive(false);
            itemPoolDic[item.name].Enqueue(item);
        }
    }

    #endregion item 缓存池管理

    #region 位置管理

    public struct ItemPos
    {
        public string name;
        public int index;
        public float startPos;
        public float endPos;
        public float spacing;
        public float offtPos;
        public float size;
        public float gridPosX;
        public float gridPosY;
        public float otherSize;
    }

    public class ItemPosMgr
    {
        private FastScrollView.ScrollType scrollType;
        private float firstPadding = 0;
        private float lastPadding = 0;
        private bool isVertical = false;
        private int itemCount = 0;
        private float totalSize = 0; // 水平则表示宽的大小， 垂直则表示高的大小
        private Dictionary<int, ItemPos> itemPosDic = new Dictionary<int, ItemPos>();

        #region 格子相关

        private bool isGrid = false;
        private int oneColOrRowItemCount = 2;
        private Padding gridPadding;
        private Vector2 gridSpacing;
        private FastScrollView.GridAlignment gridAlignment;

        #endregion 格子相关

        // 获取最后一个间隔
        public float GetLastPadding()
        {
            return lastPadding;
        }

        public ItemPosMgr(float firstPadding, float lastPadding, FastScrollView.ScrollType scrollType)
        {
            this.scrollType = scrollType;
            this.firstPadding = firstPadding;
            this.lastPadding = lastPadding;
            isVertical = scrollType == FastScrollView.ScrollType.TopToBottom || scrollType == FastScrollView.ScrollType.BottomToTop;
            Clear();
        }

        /// <summary>
        /// 初始格子信息
        /// </summary>
        public void InitGrid(FastScrollView.GridAlignment gridAlignment, int oneColOrRowItemCount, ref Padding padding, ref Vector2 spacing)
        {
            isGrid = true;
            this.gridAlignment = gridAlignment;
            gridPadding = padding;
            gridSpacing = spacing;
            this.oneColOrRowItemCount = oneColOrRowItemCount;

            if (scrollType == FastScrollView.ScrollType.TopToBottom)
            {
                firstPadding = gridPadding.top;
                lastPadding = gridPadding.bottom;
            }
            else if (scrollType == FastScrollView.ScrollType.BottomToTop)
            {
                firstPadding = gridPadding.bottom;
                lastPadding = gridPadding.top;
            }
            else if (scrollType == FastScrollView.ScrollType.LeftToRight)
            {
                firstPadding = gridPadding.left;
                lastPadding = gridPadding.right;
            }
            else
            {
                firstPadding = gridPadding.right;
                lastPadding = gridPadding.left;
            }
        }

        public void Clear(int index = 0)
        {
            if (index == 0)
            {
                totalSize = 0;
                itemCount = 0;
                itemPosDic.Clear();
            }
            else
            {
                for (int i = itemCount - 1; i >= index; i--)
                {
                    itemCount--;
                    totalSize -= itemPosDic[i].size - itemPosDic[i - 1].spacing;
                    itemPosDic.Remove(i);
                }
            }
        }

        public void SetItemCount(int itemCount)
        {
            this.itemCount = itemCount;
        }

        /// <summary>
        /// itemPosDic 里面的key，一定是0，1，2，3...这样的顺序号
        /// </summary>
        public void SetItemPos(ref ItemPos itemPos)
        {
            if (isGrid)
            {
                SetGridItemPos(ref itemPos);
                itemPosDic[itemPos.index] = itemPos;
                return;
            }

            if (scrollType == FastScrollView.ScrollType.TopToBottom || scrollType == FastScrollView.ScrollType.RightToLeft)
            {
                if (itemPos.index == 0)
                {
                    itemPos.startPos = -firstPadding;
                    itemPos.endPos = -itemPos.size - firstPadding;
                }
                else
                {
                    itemPos.startPos = itemPosDic[itemPos.index - 1].endPos - itemPosDic[itemPos.index - 1].spacing;
                    itemPos.endPos = itemPos.startPos - itemPos.size;
                }
            }
            else if (scrollType == FastScrollView.ScrollType.BottomToTop || scrollType == FastScrollView.ScrollType.LeftToRight)
            {
                // todo 整理一个，看能不能与TopToBottom合一起
                if (itemPos.index == 0)
                {
                    itemPos.startPos = firstPadding;
                    itemPos.endPos = itemPos.size + firstPadding;
                }
                else
                {
                    itemPos.startPos = itemPosDic[itemPos.index - 1].endPos + itemPosDic[itemPos.index - 1].spacing;
                    itemPos.endPos = itemPos.startPos + itemPos.size;
                }
            }
            itemPosDic[itemPos.index] = itemPos;
        }

        /// <summary>
        /// 同SetItemPos，只是这是专门针对格子的，位置不要使用公式，方便以后支持格子是可变大小
        /// </summary>
        private void SetGridItemPos(ref ItemPos itemPos)
        {
            int index = itemPos.index;

            if (scrollType == FastScrollView.ScrollType.TopToBottom || scrollType == FastScrollView.ScrollType.BottomToTop)
            {
                bool isLeftToRight = gridAlignment == FastScrollView.GridAlignment.LeftToRight;
                bool isTopToBottom = scrollType == FastScrollView.ScrollType.TopToBottom;
                if (index == 0)
                {
                    itemPos.startPos = isTopToBottom ? -firstPadding : firstPadding;
                    itemPos.endPos = isTopToBottom ? -itemPos.size - firstPadding : itemPos.size + firstPadding;
                    itemPos.gridPosX = isLeftToRight ? gridPadding.left : -gridPadding.right;
                    itemPos.gridPosY = itemPos.startPos;
                }
                else if (index < oneColOrRowItemCount)
                {
                    itemPos.startPos = isTopToBottom ? -firstPadding : firstPadding;
                    itemPos.endPos = isTopToBottom ? -itemPos.size - firstPadding : itemPos.size + firstPadding;
                    itemPos.gridPosX = isLeftToRight ? itemPosDic[index - 1].gridPosX + itemPos.otherSize + gridSpacing.x : itemPosDic[index - 1].gridPosX - itemPos.otherSize - gridSpacing.x;
                    itemPos.gridPosY = itemPos.startPos;
                }
                else
                {
                    int preIndex = index - oneColOrRowItemCount;
                    float gridPosX;
                    if (index % oneColOrRowItemCount == 0)
                        gridPosX = itemPosDic[0].gridPosX;
                    else
                        gridPosX = isLeftToRight ? itemPosDic[index - 1].gridPosX + itemPos.otherSize + gridSpacing.x : itemPosDic[index - 1].gridPosX - itemPos.otherSize - gridSpacing.x;

                    itemPos.startPos = isTopToBottom ? itemPosDic[preIndex].endPos - gridSpacing.y : itemPosDic[preIndex].endPos + gridSpacing.y;
                    itemPos.endPos = isTopToBottom ? itemPos.startPos - itemPos.size : itemPos.startPos + itemPos.size;
                    itemPos.gridPosX = gridPosX;
                    itemPos.gridPosY = itemPos.startPos;
                }
            }
            else if (scrollType == FastScrollView.ScrollType.LeftToRight || scrollType == FastScrollView.ScrollType.RightToLeft)
            {
                bool isTopToBottom = gridAlignment == FastScrollView.GridAlignment.TopToBottom;
                bool isLeftToRight = scrollType == FastScrollView.ScrollType.LeftToRight;
                if (index == 0)
                {
                    itemPos.startPos = isLeftToRight ? firstPadding : -firstPadding;
                    itemPos.endPos = isLeftToRight ? itemPos.size + firstPadding : -itemPos.size - firstPadding;
                    itemPos.gridPosX = itemPos.startPos;
                    itemPos.gridPosY = isTopToBottom ? -gridPadding.top : gridPadding.bottom;
                }
                else if (index < oneColOrRowItemCount)
                {
                    itemPos.startPos = isLeftToRight ? firstPadding : -firstPadding;
                    itemPos.endPos = isLeftToRight ? itemPos.size + firstPadding : -itemPos.size - firstPadding;
                    itemPos.gridPosX = itemPos.startPos;
                    itemPos.gridPosY = isTopToBottom ? itemPosDic[index - 1].gridPosY - itemPos.otherSize - gridSpacing.y : itemPosDic[index - 1].gridPosY + itemPos.otherSize + gridSpacing.y;
                }
                else
                {
                    int preIndex = index - oneColOrRowItemCount;
                    float gridPosY;
                    if (index % oneColOrRowItemCount == 0)
                        gridPosY = itemPosDic[0].gridPosY;
                    else
                        gridPosY = isTopToBottom ? itemPosDic[index - 1].gridPosY - itemPos.otherSize - gridSpacing.y : itemPosDic[index - 1].gridPosY + itemPos.otherSize + gridSpacing.y;

                    itemPos.startPos = isLeftToRight ? itemPosDic[preIndex].endPos + gridSpacing.x : itemPosDic[preIndex].endPos - gridSpacing.x;
                    itemPos.endPos = isLeftToRight ? itemPos.startPos + itemPos.size : itemPos.startPos - itemPos.size;
                    itemPos.gridPosX = itemPos.startPos;
                    itemPos.gridPosY = gridPosY;
                }
            }
        }

        /// <summary>
        /// 设置item位置和下一个item的位置
        /// 注意，一般调用此函数是在循环里，调用完需要在循环外调用ResetPos()
        /// </summary>
        public void SetItemPosAndNext(ref ItemPos itemPos)
        {
            SetItemPos(ref itemPos);

            if (itemPos.index >= itemCount - 1)
                return;

            itemPos = itemPosDic[itemPos.index + 1];
            SetItemPos(ref itemPos);
        }

        /// <summary>
        /// 重设位置
        /// </summary>
        public void ResetPos(int startIndex = 0)
        {
            ItemPos itemPos;
            for (int i = startIndex; i < itemCount; i++)
            {
                itemPos = itemPosDic[i];
                SetItemPos(ref itemPos);
            }
        }

        /// <summary>
        /// 计算总大小
        /// </summary>
        public float CalculateTotalSize()
        {
            if (isGrid)
                return CalculateGridTotalSize();

            totalSize = 0;
            int count = itemPosDic.Count;
            for (int i = 0; i < count; i++)
            {
                totalSize += itemPosDic[i].size + itemPosDic[i].spacing;
            }

            totalSize += firstPadding + lastPadding;
            if (count == 1)
            {
                totalSize -= itemPosDic[0].spacing;
            }
            else if (count > 1)
            {
                totalSize -= itemPosDic[count - 2].spacing;
            }

            return totalSize;
        }

        /// <summary>
        /// 计算总大小 (目前只以固定item大小来算，后续改成支持可变item再修改此处代码)
        /// </summary>
        private float CalculateGridTotalSize()
        {
            if (totalSize != 0)
                return totalSize;

            float num = Mathf.Ceil(itemPosDic.Count / (float)oneColOrRowItemCount);
            if (num == 1)
                totalSize = firstPadding + lastPadding + itemPosDic[0].size;
            else
                totalSize = firstPadding + lastPadding + itemPosDic[0].size * num + itemPosDic[0].spacing * (num - 1);

            return totalSize;
        }

        // todo:可变大小04 SetItemSize
        /// <summary>
        /// 设置item大小
        /// </summary>
        public ItemPos SetItemSize(int index, float size)
        {
            ItemPos itemPos = itemPosDic[index];
            if (size != itemPos.size)
            {
                totalSize += size - itemPos.size;
                itemPos.size = size;
            }

            return itemPos;
        }

        public Vector2 GetItemPos(int index)
        {
            ItemPos itemPos = itemPosDic[index];
            if (isGrid)
                return new Vector2(itemPos.gridPosX, itemPos.gridPosY);

            return isVertical ? new Vector2(itemPos.offtPos, itemPos.startPos) : new Vector2(itemPos.startPos, itemPos.offtPos);
        }

        public ItemPos GetItem(int index)
        {
            return itemPosDic[index];
        }

        /// <summary>
        /// 获取格子，前后边缘，假如格子1与格子2的间隔为10，那么格子2前边缘为5
        /// </summary>
        public void GetItemFrontAndBackMargin(int index, out float front, out float back)
        {
            if (index == 0)
            {
                front = firstPadding;
            }
            else
            {
                front = GetItem(index - 1).spacing * 0.5f;
            }

            if (itemCount == 1 || index == itemCount - 1 || isGridLastAlignment(index))
            {
                back = lastPadding;
            }
            else
            {
                back = GetItem(index).spacing * 0.5f;
            }
        }

        /// <summary>
        /// 是否是格子的最后一行或列
        /// </summary>
        private bool isGridLastAlignment(int index)
        {
            if (!isGrid)
                return false;

            int num = itemCount % oneColOrRowItemCount;
            if (num == 0)
                return index >= itemCount - oneColOrRowItemCount;
            return index >= itemCount - num;
        }

        /// <summary>
        /// 获取总大小， 水平则表示宽的大小， 垂直则表示高的大小
        /// </summary>
        public float GetTotalSize()
        {
            return totalSize;
        }
    }

    #endregion 位置管理

    #region 工具类

    public static class Util
    {
        #region RectTransform 设置坐标

        /// <summary>
        /// 设置锚点位置
        /// </summary>
        private static void SetAnchoredPosition(RectTransform target, Vector2 targetSize, Vector2 parentSize, Vector2 pos)
        {
            Vector2 minPos = parentSize * ((target.anchorMax - target.anchorMin) * target.pivot + target.anchorMin);
            minPos -= targetSize * target.pivot;

            target.anchoredPosition = -minPos + pos;
        }

        /// <summary>
        /// 以矩形的形状设位置，不受其anchor和pivot影响
        /// 函数编号即：小键盘数字对应矩形位置，如1表示以左下角为基准，5表示以矩形的中间为基准
        /// 小键盘数字排列：
        /// 7 8 9
        /// 4 5 6
        /// 1 2 3
        /// 如果pos为0
        /// </summary>
        public static void SetAnchoredPosition1(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition2(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;
            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos.x += parentSize.x * 0.5f - targetSize.x * 0.5f;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition3(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos.x += parentSize.x - targetSize.x;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition4(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos.y += parentSize.y * 0.5f - targetSize.y * 0.5f;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition5(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos += parentSize * 0.5f - targetSize * 0.5f;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition6(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos.x += parentSize.x - targetSize.x;
            pos.y += parentSize.y * 0.5f - targetSize.y * 0.5f;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition7(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos.y += parentSize.y - targetSize.y;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition8(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos.y += parentSize.y - targetSize.y;
            pos.x += parentSize.x * 0.5f - targetSize.x * 0.5f;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        public static void SetAnchoredPosition9(RectTransform target, Vector2 pos)
        {
            RectTransform parentRectTransform = target.parent.GetComponent<RectTransform>();
            if (parentRectTransform == null)
                return;

            Vector2 parentSize = parentRectTransform.rect.size;
            Vector2 targetSize = target.rect.size * target.localScale;
            pos += parentSize - targetSize;
            SetAnchoredPosition(target, targetSize, parentSize, pos);
        }

        #endregion RectTransform 设置坐标
    }

    #endregion 工具类
}